// this sprite system can only handle Sprite/Bone compounds with a one roote bone and 
// the sprites parented on them. the first keyframe in the mainline gives the information 
// for the construction of this hierarchy. in the timeline there must be always a first frame with id = 0
#include "SprManager.h"


SprManager::SprManager(): mCreatedQuads(),mCurrentEntity(0),mCurrentAnimation(0),
mCurrentKey(0), mNextKey(0), mData(), mTimelineTime(sf::Time::Zero), mFPS(30.0f)
{}

SprManager::SprManager(const std::string& dataFile): mCreatedQuads(), mCurrentEntity(0), mCurrentAnimation(0),
mCurrentKey(0), mNextKey(0), mData(dataFile), mTimelineTime(sf::Time::Zero), mFPS(30.0f)
{}

void SprManager::loadAnimDataFromFile(const std::string& dataFile)
{
	mData.load(dataFile);
}

Quad* SprManager::createQuadTree()
{
	//sprites: Mainline sprite objects at key 0.
	std::map<int, SCML::Data::Entity::Animation::Mainline::Key::Object_Container>& sprites = 
		mData.entities[mCurrentEntity]->animations[mCurrentAnimation]->mainline.keys[0]->objects;

	//bones: Mainline bone objects at key 0.
	std::map<int, SCML::Data::Entity::Animation::Mainline::Key::Bone_Container>& bones =
		mData.entities[mCurrentEntity]->animations[mCurrentAnimation]->mainline.keys[0]->bones;
		
	//sprTimelines: All Timelines of currentAnimation
	std::map<int, SCML::Data::Entity::Animation::Timeline*>& quadTimelines =
		mData.entities[mCurrentEntity]->animations[mCurrentAnimation]->timelines;

	// Clear all Quads
	//quadTreeRoot.clearQuads(); //clears it the whole tree or only the root children?
	mCreatedQuads.clear();   

	// Create Bones from Mainline
	for (auto boneIt = bones.begin(); boneIt != bones.end(); boneIt++)
	{
		auto boneInfo = quadTimelines[boneIt->second.bone_ref->timeline]->keys[0]->bone;
	
		Quad::Ptr bnQuadPtr(new BoneQuad(boneIt->first, boneIt->second.bone_ref->timeline));
		
		bnQuadPtr->setScale(boneInfo.scale_x, boneInfo.scale_y);
		bnQuadPtr->setRotation(360.f - boneInfo.angle);
		bnQuadPtr->setPosition(boneInfo.x, -boneInfo.y);
		
		mCreatedQuads.insert(std::make_pair(bnQuadPtr->getID(),std::move(bnQuadPtr)));

	}

	// Create Sprites from Mainline and connect to Bones

	for (auto sprIt = sprites.begin(); sprIt != sprites.end(); sprIt++)
	{	
		// Object in timeline of sprite
		SCML::Data::Entity::Animation::Timeline::Key::Object sprObject = 
			quadTimelines[sprIt->second.object_ref->timeline]->keys[0]->object;

		// File pointer of sprite
		SCML::Data::Folder::File* sprFile = 
			mData.folders[sprObject.folder]->files[sprObject.file];

		Quad::Ptr sprQuadPtr(new SprQuad(
			sprIt->second.object_ref->id,			//object id
			sprIt->second.object_ref->timeline,		//timeline id
			sprFile->width,
			sprFile->height,
			sf::Vector2f(sprFile->v1x, sprFile->v1y),			
			sf::Vector2f(sprFile->v2x, sprFile->v2y),			
			sf::Vector2f(sprFile->v3x, sprFile->v3y),				
			sf::Vector2f(sprFile->v4x, sprFile->v4y),			
			sf::Vector2f(sprFile->pivot_x, sprFile->pivot_y),
			sprIt->second.object_ref->z_index));

		//Set transformation data of sprite from scml
		sprQuadPtr->setScale(sprObject.scale_x, sprObject.scale_y);
		sprQuadPtr->setRotation(360 - sprObject.angle);
		sprQuadPtr->setPosition(sprObject.x, -sprObject.y);
		sprQuadPtr->setOrigin(sprObject.pivot_x, sprObject.pivot_y);

		// Connect Sprites to Bones
		if(!mCreatedQuads.empty())
		{
			mCreatedQuads[sprIt->second.object_ref->parent]->attachChild(std::move(sprQuadPtr));
		}
	}

	// Parent Bones
	for (auto bone2It = bones.crbegin(); bone2It != bones.crend(); ++bone2It)
	{
		if (bone2It->second.bone_ref->parent >= 0)
		{	
			mCreatedQuads.at(bone2It->second.bone_ref->parent)->attachChild(std::move(mCreatedQuads.at(bone2It->first)));
			mCreatedQuads.erase(bone2It->first);
		}
		else if((bone2It->second.bone_ref->parent < 0) && (mCreatedQuads.size() > 1))
		{
			throw std::logic_error("the bones are not in order");
		}
	}
	return mCreatedQuads[0].get();
}
	

void SprManager::updateQuadTree(sf::Time dt, Quad* rootQuad)
{
	//Current Animation
	SCML::Data::Entity::Animation* currentAnimation = mData.entities[mCurrentEntity]->animations[mCurrentAnimation];

	//Mainline
	SCML::Data::Entity::Animation::Mainline& mMainline = currentAnimation->mainline;

	//Mainline Keys
	std::map<int, SCML::Data::Entity::Animation::Mainline::Key*>& mainlineKeys = mMainline.keys;

	//QuadTimelines: All Timelines of currentAnimation
	std::map<int, SCML::Data::Entity::Animation::Timeline*>& quadTimelines =
		mData.entities[mCurrentEntity]->animations[mCurrentAnimation]->timelines;	

	mTimelineTime += dt;

	//loop to frame 0
	if (((static_cast<float>(currentAnimation->length)) / mFPS) < mTimelineTime.asSeconds())
	{
		mTimelineTime -= sf::seconds(static_cast<float>(currentAnimation->length) / mFPS);
	}

	mCurrentKey = getCurrentKeyId(mTimelineTime);
	mNextKey = getNextKeyId();

	float timeFraction;

	if (mCurrentKey > mNextKey)
	{
		timeFraction = (mTimelineTime.asSeconds() - (static_cast<float>(mainlineKeys[mCurrentKey]->time) / mFPS)) /
			((static_cast<float>(currentAnimation->length) / mFPS) - (static_cast<float>(mainlineKeys[mCurrentKey]->time) / mFPS));
	}
	else
	{
		// fraction 0 to 1.0 of time between keys. 
		timeFraction = (mTimelineTime.asSeconds() - (static_cast<float>(mainlineKeys[mCurrentKey]->time) / mFPS)) /
			((static_cast<float>(mainlineKeys[mNextKey]->time) / mFPS) - (static_cast<float>(mainlineKeys[mCurrentKey]->time) / mFPS));
	}

	

	// update sprite transformations
	for (auto itr = mainlineKeys[mCurrentKey]->objects.begin(); itr != mainlineKeys[mCurrentKey]->objects.end(); ++itr)
	{		
		auto source_obj = quadTimelines[itr->second.object_ref->timeline]->keys[itr->second.object_ref->key]->object;
		sf::Vector2f source_scale = sf::Vector2f(source_obj.scale_x, source_obj.scale_y);
		float source_angle = source_obj.angle;
		sf::Vector2f source_pos = sf::Vector2f(source_obj.x, source_obj.y);
		int source_spin = quadTimelines[itr->second.object_ref->timeline]->keys[itr->second.object_ref->key]->spin;

		
		auto dest_obj = quadTimelines[itr->second.object_ref->timeline]->keys[mainlineKeys[mNextKey]->objects[itr->second.object_ref->id].object_ref->key]->object;
		sf::Vector2f dest_scale = sf::Vector2f(dest_obj.scale_x, dest_obj.scale_y);
		sf::Vector2f dest_pos = sf::Vector2f(dest_obj.x, dest_obj.y);			
		float dest_angle = dest_obj.angle;


		sf::Vector2f currentPos = sf::Vector2f(lerp(source_pos.x, dest_pos.x, timeFraction), -lerp(source_pos.y, dest_pos.y, timeFraction));
		sf::Vector2f currentScale = sf::Vector2f(lerp(source_scale.x, dest_scale.x, timeFraction), lerp(source_scale.y, dest_scale.y, timeFraction));
		float currentAngle = source_angle;
		if (source_spin != 0)
		{
			if (source_spin  > 0 && source_angle > dest_angle)
				currentAngle = lerp(source_angle, dest_angle + 360, timeFraction);
			else if (source_spin  < 0 && source_angle <  dest_angle)
				currentAngle = lerp(source_angle, dest_angle - 360, timeFraction);
			else
				currentAngle = lerp(source_angle, dest_angle, timeFraction);
		}
			

		rootQuad->setTransformByID(itr->first, Quad::Type::Sprite, currentPos, 360.f-currentAngle, currentScale);

		// TODO: if clause to see if it needs to change sprite

		static_cast<SprQuad*>(rootQuad->getQuadById(itr->first, Quad::Type::Sprite))->setDimensions(
			mData.folders[source_obj.folder]->files[source_obj.file]->width,
			mData.folders[source_obj.folder]->files[source_obj.file]->height,
			sf::Vector2f(mData.folders[source_obj.folder]->files[source_obj.file]->pivot_x,
			mData.folders[source_obj.folder]->files[source_obj.file]->pivot_y));

		static_cast<SprQuad*>(rootQuad->getQuadById(itr->first, Quad::Type::Sprite))->setTexCoord(
			sf::Vector2f(mData.folders[source_obj.folder]->files[source_obj.file]->v1x, mData.folders[source_obj.folder]->files[source_obj.file]->v1y),
			sf::Vector2f(mData.folders[source_obj.folder]->files[source_obj.file]->v2x, mData.folders[source_obj.folder]->files[source_obj.file]->v2y),
			sf::Vector2f(mData.folders[source_obj.folder]->files[source_obj.file]->v3x, mData.folders[source_obj.folder]->files[source_obj.file]->v3y),
			sf::Vector2f(mData.folders[source_obj.folder]->files[source_obj.file]->v4x, mData.folders[source_obj.folder]->files[source_obj.file]->v4y));
			
	}

	// update bone transformations
	for (auto itr2 = mainlineKeys[mCurrentKey]->bones.begin(); itr2 != mainlineKeys[mCurrentKey]->bones.end(); ++itr2)
	{
		auto source_bone = quadTimelines[itr2->second.bone_ref->timeline]->keys[itr2->second.bone_ref->key]->bone;
		sf::Vector2f source_bscale = sf::Vector2f(source_bone.scale_x, source_bone.scale_y);
		sf::Vector2f source_bpos = sf::Vector2f(source_bone.x, source_bone.y);		
		float source_bangle = source_bone.angle;
		int source_bspin = quadTimelines[itr2->second.bone_ref->timeline]->keys[itr2->second.bone_ref->key]->spin;

		auto dest_bone = quadTimelines[itr2->second.bone_ref->timeline]->keys[mainlineKeys[mNextKey]->bones[itr2->second.bone_ref->id].bone_ref->key]->bone;
		sf::Vector2f dest_bscale = sf::Vector2f(dest_bone.scale_x, dest_bone.scale_y);
		float dest_bangle = dest_bone.angle;
		sf::Vector2f dest_bpos = sf::Vector2f(dest_bone.x, dest_bone.y);
			


		sf::Vector2f currentbPos = sf::Vector2f(lerp(source_bpos.x, dest_bpos.x, timeFraction), -lerp(source_bpos.y, dest_bpos.y, timeFraction));
		sf::Vector2f currentbScale = sf::Vector2f(lerp(source_bscale.x, dest_bscale.x, timeFraction), lerp(source_bscale.y, dest_bscale.y, timeFraction));
		float currentbAngle = source_bangle;
		if (source_bspin != 0)
		{
			if (source_bspin > 0 && source_bangle > dest_bangle)
				currentbAngle = lerp(source_bangle, dest_bangle + 360, timeFraction);
			else if (source_bspin < 0 && source_bangle < dest_bangle)
				currentbAngle = lerp(source_bangle, dest_bangle - 360, timeFraction);
			else
				currentbAngle = lerp(source_bangle, dest_bangle, timeFraction);
		}

		rootQuad->setTransformByID(itr2->first, Quad::Type::Bone, currentbPos, 360.f-currentbAngle, currentbScale);
		
	}

}

int SprManager::getAnimation() const
{
	return mCurrentAnimation;
}

void SprManager::setAnimation(int i)
{
	mCurrentAnimation = i;
}


float SprManager::getFPS() const
{
	return mFPS;
}

void SprManager::setFPS(float fps)
{
	mFPS = fps;
}

int SprManager::getCurrentKeyId(sf::Time loopedtime) //time is after looping. so time is always less than timelength of animation.
{
	std::map<int, SCML::Data::Entity::Animation::Mainline::Key*>& mainlineKeys =
		mData.entities[mCurrentEntity]->animations[mCurrentAnimation]->mainline.keys;

	for (auto itr = mainlineKeys.begin(); itr != mainlineKeys.end(); ++itr)
	{
		auto tempItr = itr;
		if ( ++tempItr != mainlineKeys.end())
		{
			if (((static_cast<float>(itr->second->time) / mFPS) <= loopedtime.asSeconds()) && 
				((static_cast<float>(tempItr->second->time) / mFPS) > loopedtime.asSeconds()))
				return itr->first;
			else continue;
		}
		else
		{
			return itr->first;
		}		
	}
}

int	SprManager::getNextKeyId() 
{
	std::map<int, SCML::Data::Entity::Animation::Mainline::Key*>& mainlineKeys =
		mData.entities[mCurrentEntity]->animations[mCurrentAnimation]->mainline.keys;

	if (mainlineKeys.size() > static_cast<unsigned>(mCurrentKey + 1)) return (mCurrentKey + 1);
	else return 0;
}